<!--用户发帖页面-->
<template>
    <div class="to-post">
        <!-- 顶部栏 -->
        <header class="top-bar">
            <button class="icon-btn back" @click="goBack">←</button>
            <div class="top-title">内容</div>
            <button class="text-btn publish" @click="onPublish">发布</button>
        </header>

        <!-- 主体内容 -->
        <main class="page-body">
            <!-- 媒体网格：预览与添加按钮同一网格，按钮会后移 -->
            <div class="media-grid">
                <!-- 图片预览 -->
                <div
                        v-for="(src, idx) in imagePreviews"
                        :key="'img-'+idx"
                        class="preview-item"
                        draggable="true"
                        @dragstart="onDragStart(idx)"
                        @dragover.prevent
                        @drop="onDrop(idx)"
                >
                    <img :src="src" alt="preview"/>
                    <button class="del-btn" @click.stop="removeImage(idx)">×</button>
                </div>
                <!-- 视频预览（与方块同尺寸） -->
                <div v-if="videoPreviewUrl" class="preview-item">
                    <video :src="videoPreviewUrl" controls></video>
                    <button class="del-btn" @click.stop="removeVideo()">×</button>
                </div>
                <!-- 添加图片按钮：仅当未选择视频，且数量未满 -->
                <label v-if="!videoPreviewUrl && imagePreviews.length < MAX_IMAGES" class="picker-square">
                    <input class="file-input" type="file" accept="image/*" multiple @change="onSelectImages"/>
                    <div class="add-box">+</div>
                </label>
                <!-- 添加视频按钮：仅当未选择任何图片与视频时显示，且只允许1个 -->
                <label v-if="!videoPreviewUrl && imagePreviews.length === 0" class="picker-square">
                    <input class="file-input" type="file" accept="video/*" @change="onSelectVideo"/>
                    <div class="add-box">🎬</div>
                </label>
            </div>

            <div class="form-block">
                <div class="title-row">
                    <input
                            v-model="title"
                            class="title-input"
                            type="text"
                            placeholder="填写标题"
                            maxlength="50"
                    />
                    <span class="counter">{{ 50 - title.length }}</span>
                </div>
                <textarea
                        v-model="contentText"
                        ref="contentTextareaRef"
                        class="content-input"
                        rows="8"
                        placeholder="添加正文"
                />
            </div>
        </main>

        <!-- 底部操作区 -->
        <div class="bottom-actions">
            <button class="add-topic" @click="openGameSheet">{{ selectedGame?.label || '+ 添加分区及话题' }}</button>
        </div>

        <!-- 工具栏 -->
        <footer class="toolbar">
            <button class="tool-btn" @click="toggleEmoji">😊</button>
            <div class="flex-spacer"></div>
            <button class="tool-btn" @click="openVisibilitySheet">⚙️</button>
        </footer>

        <!-- 表情弹窗 -->
        <div v-if="showEmoji" class="emoji-popup">
            <button v-for="e in emojis" :key="e" class="emoji-btn" @click="appendEmoji(e)">{{ e }}</button>
        </div>

        <!-- 游戏分区上拉选择框 -->
        <div v-if="showGameSheet" class="sheet-mask" @click.self="showGameSheet=false">
            <div class="sheet">
                <div class="sheet-title">选择游戏分区</div>
                <div v-if="gamesLoading" class="sheet-item" style="text-align:center;border:none;">加载中…</div>
                <template v-else>
                    <div v-if="gameOptions.length === 0" class="sheet-item" style="text-align:center;border:none;">
                        加载失败或暂无数据
                        <button class="sheet-cancel" style="margin-top:8px;" @click="reloadGames">重试</button>
                    </div>
                    <button v-for="opt in gameOptions" :key="opt.id" class="sheet-item" @click="selectGame(opt)">
                        {{ opt.label }}
                    </button>
                </template>
                <button class="sheet-cancel" @click="showGameSheet=false">取消</button>
            </div>
        </div>

        <!-- 权限上拉选择框 -->
        <div v-if="showVisibilitySheet" class="sheet-mask" @click.self="showVisibilitySheet=false">
            <div class="sheet">
                <div class="sheet-title">选择可见范围</div>
                <button v-for="opt in visibilityOptions" :key="opt.id" class="sheet-item"
                        @click="selectVisibility(opt)">{{ opt.label }}
                </button>
                <button class="sheet-cancel" @click="showVisibilitySheet=false">取消</button>
            </div>
        </div>

        <!-- 轻提示 -->
        <transition name="fade">
            <div v-if="toastMsg" class="toast">{{ toastMsg }}</div>
        </transition>
    </div>

</template>

<script setup lang="ts">
import {ref, onMounted} from 'vue'
import {useRouter, useRoute} from 'vue-router'
import axios from '@/utils/axios'
import {client as ossClient, getFileNameUUID} from '@/utils/alioss.ts'

const router = useRouter()
const route = useRoute()

// 记录来源页面，用于发布成功后返回
const fromPage = ref<string>('/home')

// 组件挂载时记录来源页面
onMounted(() => {
    // 未登录强制跳转到登录页（双保险）
    const rawToken = (localStorage.getItem('token') || sessionStorage.getItem('token') || '').trim()
    const tokenValid = rawToken && rawToken !== 'undefined' && rawToken !== 'null'
    const rawUserId = (localStorage.getItem('userId') || '').trim()
    const userIdValid = rawUserId && rawUserId !== '0' && rawUserId !== 'undefined' && rawUserId !== 'null'
    if (!tokenValid || !userIdValid) {
        const redirect = encodeURIComponent(route.fullPath)
        router.replace({ path: '/login', query: { redirect } })
        return
    }

    // 从路由查询参数中获取来源页面，如果没有则使用默认值
    const from = route.query.from as string
    if (from && from !== '/toPost') {
        fromPage.value = from
    } else {
        // 如果没有来源页面参数，尝试从浏览器历史记录中获取
        // 这里使用 sessionStorage 来记录上一个页面
        const lastPage = sessionStorage.getItem('lastPage')
        if (lastPage && lastPage !== '/toPost') {
            fromPage.value = lastPage
        }
    }
    console.log('发帖页面来源:', fromPage.value)
})

const title = ref<string>('')// 标题
const contentText = ref<string>('')// 内容
const selectedFiles = ref<File[]>([])// 已选择的文件
const imagePreviews = ref<string[]>([])// 图片预览链接
const videoFile = ref<File | null>(null)// 已选择的视频文件
const videoPreviewUrl = ref<string>('')// 视频预览链接
const MAX_VIDEO_MB = 100 // 视频最大大小（MB）
const MAX_IMAGES = 9 // 最大图片数量
const MAX_IMAGE_MB = 5 // 单张图片最大大小（MB）
const contentTextareaRef = ref<HTMLTextAreaElement | null>(null)// 内容文本域引用
const showEmoji = ref<boolean>(false)// 是否显示表情弹窗
const emojis = ['😀', '😂', '🥰', '😎', '🤔', '😭', '👍', '👏', '🔥', '🌟', '🍀', '🎮', '❤️', '🚀', '🐱', '🐶']// 表情列表
const draggingIndex = ref<number | null>(null)// 拖拽中的图片索引
const toastMsg = ref<string>('')// 轻提示内容
let toastTimer: number | null = null// 轻提示定时器
const publishing = ref<boolean>(false)// 是否正在发布

// 选择面板与选项
type Option = { id: number; label: string }// 选项类型
const showGameSheet = ref<boolean>(false)// 是否显示游戏分区选择面板
const showVisibilitySheet = ref<boolean>(false)// 是否显示可见范围选择面板
const gameOptions = ref<Option[]>([])// 游戏分区选项
const gamesLoaded = ref<boolean>(false)// 游戏分区是否已加载
const gamesLoading = ref<boolean>(false)// 游戏分区是否正在加载
const visibilityOptions = ref<Option[]>([// 可见范围选项
    {id: 1, label: '公开'},
    {id: 2, label: '仅关注可见'},
    {id: 3, label: '仅自己可见'},
])
const selectedGame = ref<Option | null>(null)// 已选择的游戏分区
const selectedVisibility = ref<Option | null>(null)// 已选择的可见范围

function goBack(): void {
    // 返回到来源页面，如果没有来源页面则使用默认的首页
    router.push(fromPage.value)
}

async function onPublish(): Promise<void> {// 发布帖子
    if (publishing.value) return
    const t = title.value.trim()
    const c = contentText.value.trim()
    if (!t || !c) {
        showToast('标题/内容不能为空哦~')
        return
    }
    if (t.length > 50) {
        showToast('标题不能超过50个字哦~')
        return
    }
    if (c.length > 500) {
        showToast('内容不能超过500个字哦~')
        return
    }
    if (!selectedGame.value) {
        showToast('请选择游戏分区')
        return
    }
    if (!selectedVisibility.value) {
        showToast('请设置可见范围')
        return
    }

    publishing.value = true
    showToast('开始上传…')
    try {
        const {mediaType, urls} = await uploadAllMedia() // 上传所有媒体文件
        const payload: Record<string, any> = { // 准备发布帖子的数据
            userId: Number(localStorage.getItem('userId')),// 用户id
            title: t,// 标题
            content: c,// 内容
            gameId: selectedGame.value.id,// 游戏分区id
            visibility: selectedVisibility.value.id //权限
        }
        
        try {
            const res = await axios.post('/api/posts/add', payload)
            console.log('发布帖子请求:', payload)
            console.log('发布帖子响应:', res)
            
            // 由于axios响应拦截器已经处理了错误，这里res已经是成功的数据
            if (res && res.code === 200) {
                console.log('完整的响应数据结构:', res)
                console.log('res.data 内容:', res.data)

                // 统一从 res.data 中解析帖子ID，兼容数字或对象
                const raw = res.data as any
                let postId: number | null = null

                if (typeof raw === 'number') {
                    postId = raw
                } else if (typeof raw?.data === 'number') {
                    postId = raw.data
                } else {
                    postId = raw?.postId || raw?.id || raw?.data?.postId || raw?.data?.id || null
                }

                console.log('获取到的帖子ID:', postId)
                
                if (postId && urls.length > 0) {
                    // 上传图片/视频到PostImages表
                    await uploadImagesToDatabase(postId, urls, mediaType)
                } else if (urls.length > 0) {
                    console.error('无法获取帖子ID，图片无法保存到数据库')
                    showToast('帖子发布成功，但图片保存失败')
                }
                showToast('发布成功')
                setTimeout(() => router.push(fromPage.value), 600) // 发布成功后跳转回来源页面
            } else {
                showToast((res && (res.msg || res.message)) || '发布失败')
            }
        } catch (axiosError: any) {
            console.error('发布帖子请求失败:', axiosError)
            showToast(axiosError?.message || '发布帖子失败，请稍后再试')
        }
    } catch (e: any) {
        console.error('发布流程失败:', e)
        showToast(e?.message || '上传失败，请稍后再试')
    } finally {
        publishing.value = false
    }
}

// 上传所有媒体文件到oss服务器上
async function uploadAllMedia(): Promise<{ mediaType: 'image' | 'video' | 'none'; urls: string[] }> {
    if (selectedFiles.value.length === 0 && !videoFile.value) {
        return {mediaType: 'none', urls: []}
    }
    const client = ossClient()
    const uploadedUrls: string[] = []
    if (videoFile.value) {
        const file = videoFile.value
        const ext = (file.name.split('.').pop() || 'mp4').toLowerCase()
        const objectKey = `posts/videos/${getFileNameUUID()}.${ext}`
        const r = await client.put(objectKey, file)
        uploadedUrls.push(r.url || r.res?.requestUrls?.[0] || '')
        return {mediaType: 'video', urls: uploadedUrls}
    }
    for (const file of selectedFiles.value) {
        const name = file.name || 'image.jpg'
        const ext = (name.split('.').pop() || 'jpg').toLowerCase()
        const objectKey = `posts/images/${getFileNameUUID()}.${ext}`
        const r = await client.put(objectKey, file)
        uploadedUrls.push(r.url || r.res?.requestUrls?.[0] || '')
    }
    return {mediaType: 'image', urls: uploadedUrls}
}

// 上传图片/视频信息到数据库
async function uploadImagesToDatabase(postId: number, urls: string[], mediaType: 'image' | 'video' | 'none'): Promise<void> {
    if (mediaType === 'none' || urls.length === 0) {
        console.log('没有图片/视频,需要上传')
        return
    }
    
    try {
        // 为每张图片/视频创建记录
        for (let i = 0; i < urls.length; i++) {
            const imageData = {
                postId: postId,
                imageUrl: urls[i],
                sortOrder: (i + 1) as number // 位置下标从1开始，确保为数字类型
            }
            
            console.log(`上传第${i + 1}张图片/视频:`, imageData)
            
            try {
                const res = await axios.post('/api/postImages/add', imageData)
                console.log(`第${i + 1}张图片/视频接口响应:`, res)
                
                // 由于axios响应拦截器已经处理了错误，这里res已经是成功的数据
                if (res && res.code === 200) {
                    console.log(`第${i + 1}张图片/视频保存成功`)
                } else {
                    console.error(`第${i + 1}张图片/视频上传失败:`, res)
                    showToast(`第${i + 1}张图片/视频保存失败`)
                }
            } catch (axiosError: any) {
                console.error(`第${i + 1}张图片/视频请求失败:`, axiosError)
                showToast(`第${i + 1}张图片/视频保存失败: ${axiosError?.message || '网络错误'}`)
            }
        }
        console.log('所有图片/视频信息已保存到数据库')
    } catch (e: any) {
        console.error('保存图片/视频信息到数据库失败:', e)
        showToast('图片/视频信息保存失败')
    }
}

// 拉取游戏分区选项
async function fetchGames(): Promise<void> {
    try {
        gamesLoading.value = true
        let res: any
        
        try {
            res = await axios.post('/api/games/page')
            console.log('获取游戏列表响应:', res)
            const pageData = (res && (res.data || res.result || res.page)) || res
            const records = (pageData && (pageData.records || pageData.list || pageData.items)) || []
            gameOptions.value = records
                .map((it: any) => {
                    const id = it?.id ?? it?.gameId
                    const name = it?.name ?? it?.gameName ?? it?.title
                    if (id == null || !name) return null
                    return {id, label: String(name)}
                })
                .filter(Boolean) as Option[]
            gamesLoaded.value = true
        } catch (axiosError: any) {
            console.error('获取游戏列表请求失败:', axiosError)
            showToast(`获取游戏分区失败: ${axiosError?.message || '网络错误'}`)
            gamesLoaded.value = true
        }
    } catch (e) {
        console.error('获取游戏分区失败:', e)
        showToast('获取游戏分区失败')
        gamesLoaded.value = true
    } finally {
        gamesLoading.value = false
    }
}

function reloadGames(): void {
    gamesLoaded.value = false
    gameOptions.value = []
    fetchGames()
}

function onSelectImages(event: Event): void {
    const input = event.target as HTMLInputElement
    if (!input || !input.files) return
    if (videoPreviewUrl.value) {
        showToast('已选择视频，不能再添加图片')
        input.value = ''
        return
    }
    const files = Array.from(input.files)
    const remain = MAX_IMAGES - imagePreviews.value.length
    const slice = files.slice(0, Math.max(0, remain))
    const valid: File[] = []
    slice.forEach(f => {
        const sizeMb = f.size / (1024 * 1024)
        if (sizeMb <= MAX_IMAGE_MB) {
            valid.push(f)
        }
    })
    if (valid.length === 0) {
        showToast('图片大小超限或数量已满')
        input.value = ''
        return
    }
    selectedFiles.value = selectedFiles.value.concat(valid)
    valid.forEach(file => {
        const url = URL.createObjectURL(file)
        imagePreviews.value.push(url)
    })
    input.value = ''
}

function onSelectVideo(event: Event): void {
    const input = event.target as HTMLInputElement
    const files = input?.files
    if (!files || files.length === 0) return
    if (imagePreviews.value.length > 0) {
        showToast('已添加图片，不能再添加视频')
        input.value = ''
        return
    }
    const file = files[0] as File
    const sizeMb = file.size / (1024 * 1024)
    if (sizeMb > MAX_VIDEO_MB) {
        showToast(`视频大小超出限制（最大 ${MAX_VIDEO_MB}MB）`)
        input.value = ''
        return
    }
    videoFile.value = file
    videoPreviewUrl.value = URL.createObjectURL(file as Blob)
}

function toggleEmoji(): void {
    showEmoji.value = !showEmoji.value
}

function appendEmoji(e: string): void {
    const el = contentTextareaRef.value
    if (!el) {
        contentText.value += e
        return
    }
    const start = el.selectionStart ?? contentText.value.length
    const end = el.selectionEnd ?? contentText.value.length
    const before = contentText.value.slice(0, start)
    const after = contentText.value.slice(end)
    contentText.value = `${before}${e}${after}`
    // 将光标移动到表情后
    requestAnimationFrame(() => {
        const pos = start + e.length
        el.setSelectionRange(pos, pos)
        el.focus()
    })
    // 选择后自动关闭表情弹窗
    showEmoji.value = false
}

function removeImage(index: number): void {
    imagePreviews.value.splice(index, 1)
    selectedFiles.value.splice(index, 1)
}

function removeVideo(): void {
    if (videoPreviewUrl.value) URL.revokeObjectURL(videoPreviewUrl.value)
    videoPreviewUrl.value = ''
    videoFile.value = null
}

function onDragStart(index: number): void {
    draggingIndex.value = index
}

function onDrop(targetIndex: number): void {
    if (draggingIndex.value === null || draggingIndex.value === targetIndex) return
    const img = imagePreviews.value.splice(draggingIndex.value, 1)[0] as string
    const file = selectedFiles.value.splice(draggingIndex.value, 1)[0] as File
    imagePreviews.value.splice(targetIndex, 0, img)
    selectedFiles.value.splice(targetIndex, 0, file)
    draggingIndex.value = null
}

function showToast(msg: string): void {
    toastMsg.value = msg
    if (toastTimer) window.clearTimeout(toastTimer)
    toastTimer = window.setTimeout(() => {
        toastMsg.value = ''
    }, 1600)
}

// 打开/选择 处理函数
function openGameSheet(): void {
    if (!gamesLoaded.value) {
        fetchGames().finally(() => {
            showGameSheet.value = true
        })
        return
    }
    showGameSheet.value = true
}

function openVisibilitySheet(): void {
    showVisibilitySheet.value = true
}

function selectGame(opt: Option): void {
    selectedGame.value = opt
    showGameSheet.value = false
}

function selectVisibility(opt: Option): void {
    selectedVisibility.value = opt
    showVisibilitySheet.value = false
}
</script>

<style scoped>
/* 基础布局 */
.to-post {
    display: flex;
    flex-direction: column;
    height: 100vh;
    background: #ffffff;
}

.top-bar {
    position: sticky;
    top: 0;
    z-index: 10;
    display: flex;
    align-items: center;
    justify-content: space-between;
    height: 48px;
    padding: 0 12px;
    background: #ffffff;
    border-bottom: 1px solid #f2f3f5;
}

.icon-btn,
.text-btn {
    background: transparent;
    border: none;
    padding: 6px 8px;
    font-size: 16px;
}

.top-title {
    font-size: 18px;
    font-weight: 600;
}

.publish {
    color: #111;
    font-weight: 600;
}

.page-body {
    flex: 1;
    overflow-y: auto;
    padding: 12px 12px 0 12px;
}

.media-grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 8px;
}

.picker-square {
    width: 100%;
    aspect-ratio: 1 / 1;
    border-radius: 8px;
    background: #f6f7f8;
    display: flex;
    align-items: center;
    justify-content: center;
    position: relative;
    overflow: hidden;
}

.file-input {
    position: absolute;
    inset: 0;
    opacity: 0;
    cursor: pointer;
}

.add-box {
    font-size: 48px;
    color: #bfc2c7;
    line-height: 1;
    user-select: none;
}

/* 移除旧的文字配图按钮样式 */

.form-block {
    margin-top: 16px;
}

.title-row {
    display: flex;
    align-items: center;
    border-bottom: 2px solid #e5e6eb;
}

.title-input {
    flex: 1;
    height: 44px;
    border: none;
    outline: none;
    font-size: 16px;
}

.counter {
    color: #9aa1a9;
    font-size: 14px;
    padding-left: 8px;
}

.content-input {
    width: 100%;
    border: none;
    outline: none;
    font-size: 16px;
    padding: 12px 0;
    resize: none;
    min-height: 160px;
}

.bottom-actions {
    padding: 8px 12px;
    display: flex;
    gap: 8px;
    align-items: center;
}

.add-topic {
    flex: 1;
    height: 32px;
    border-radius: 6px;
    border: 1px dashed #e5e6eb;
    background: #ffffff;
    color: #4e5969;
}

.perm-btn {
    height: 32px;
    padding: 0 10px;
    border-radius: 6px;
    border: 1px solid #e5e6eb;
    background: #ffffff;
    color: #4e5969;
}

.toolbar {
    display: flex;
    align-items: center;
    padding: 6px 12px;
    border-top: 1px solid #f2f3f5;
}

.tool-btn {
    background: transparent;
    border: none;
    font-size: 20px;
    padding: 8px;
}

.flex-spacer {
    flex: 1;
}

/* 预览方块 */
.preview-item {
    width: 100%;
    aspect-ratio: 1 / 1;
    border-radius: 8px;
    overflow: hidden;
    background: #f6f7f8;
    position: relative;
}

.preview-item img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}

.preview-item video {
    width: 100%;
    height: 100%;
    object-fit: cover;
}

/* 选择器使用 .picker-square */

/* 表情弹窗 */
.emoji-popup {
    position: fixed;
    left: 12px;
    bottom: 60px;
    width: 280px;
    max-height: 220px;
    padding: 8px;
    background: #ffffff;
    border: 1px solid #e5e6eb;
    border-radius: 8px;
    box-shadow: 0 6px 20px rgba(0, 0, 0, 0.08);
    display: grid;
    grid-template-columns: repeat(8, 1fr);
    gap: 6px;
    overflow: auto;
}

.emoji-btn {
    background: #fff;
    border: 1px solid #f0f0f0;
    border-radius: 6px;
    padding: 6px 0;
    font-size: 18px;
}

.del-btn {
    position: absolute;
    top: 4px;
    right: 4px;
    width: 22px;
    height: 22px;
    line-height: 20px;
    text-align: center;
    background: rgba(0, 0, 0, 0.55);
    color: #fff;
    border: none;
    border-radius: 50%;
    font-size: 14px;
}

/* 上拉选择框样式 */
.sheet-mask {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.35);
    display: flex;
    align-items: flex-end;
    z-index: 20;
}

.sheet {
    width: 100%;
    background: #fff;
    border-top-left-radius: 12px;
    border-top-right-radius: 12px;
    padding: 8px 12px 12px 12px;
}

.sheet-title {
    text-align: center;
    padding: 6px 0 10px 0;
    color: #4e5969;
}

.sheet-item {
    width: 100%;
    height: 40px;
    border: 1px solid #eee;
    background: #fff;
    border-radius: 8px;
    margin-bottom: 8px;
}

.sheet-cancel {
    width: 100%;
    height: 40px;
    border: none;
    background: #f5f6f7;
    border-radius: 8px;
}

.toast {
    position: fixed;
    left: 50%;
    bottom: 90px;
    transform: translateX(-50%);
    background: rgba(0, 0, 0, 0.8);
    color: #fff;
    padding: 8px 12px;
    border-radius: 6px;
    font-size: 14px;
}

.fade-enter-active, .fade-leave-active {
    transition: opacity .2s ease;
}

.fade-enter-from, .fade-leave-to {
    opacity: 0;
}
</style>